/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.xml.parser;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import de.mnit.basis.daten.WandleArray;
import de.mnit.basis.daten.WandleFolge;
import de.mnit.basis.daten.struktur.S_Folge;
import de.mnit.basis.daten.struktur.liste.Liste;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.schluessel.S_SchluesselListe;
import de.mnit.basis.daten.struktur.schluessel.SchluesselListe;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.fehler.system.F_Sys_Datei;
import de.mnit.basis.sys.event.A_HatZuhoerer;
import de.mnit.basis.sys.event.S_Zuhoerer;

public class XMLParser extends A_HatZuhoerer<XMLParser> {

	private S_Liste<String> filter=Liste.neu();
	private boolean alle;


	public void parse(String datei) throws F_Sys_Datei, XMLStreamException {
		try {
			parse(new FileInputStream(datei));
		}
		catch(FileNotFoundException e) {
			Fehler.weitergeben(e, "Fehler beim Dateizugriff!");
		}
	}

	public void zuhoererPlus(String[] element, S_Zuhoerer<XMLElement> z) {
		for(int i=0; i<element.length; i++)
			element[i]=element[i].toLowerCase();
		String epfad=iString(element);
		this.zuhoererPlus(epfad, z);
		filter.plus(epfad);
	}

	public void zuhoererPlus(S_Zuhoerer<XMLElement> z) {
		this.zuhoererPlus("ALLE", z);
		alle=true;
	}

	public void zuhoererPlusTagEnde(String[] element, S_Zuhoerer<XMLElement> z) {
		for(int i=0; i<element.length; i++)
			element[i]=element[i].toLowerCase();
		String epfad=iString(element);
		this.zuhoererPlus("ENDE:"+epfad, z);
		filter.plus(epfad);
	}

	public void parse(InputStream is) throws XMLStreamException {
		XMLInputFactory factory2 = XMLInputFactory.newInstance();
		XMLStreamReader parser = factory2.createXMLStreamReader(is);

		S_Liste<String> pfad=Liste.neu();
		S_SchluesselListe<String, XMLElement> offen=SchluesselListe.neu();
		XMLElement element;
		while(parser.hasNext()) {
			int event = parser.next();
		    if(event==XMLStreamConstants.START_ELEMENT) {
//		      	Ausgabe.text("O: "+parser.getLocalName());
		    	String aktuell=parser.getLocalName().toLowerCase();
	      		pfad.plus(aktuell);
	      		String pfadname=iString(pfad);
	      		if(!alle && !filter.kennt(pfadname))
	      			continue;
	      		element=new XMLElement(pfad.zuArray(String.class));
	      		offen.plus(pfadname, element);
	      		if(parser.getAttributeCount()>0) {
	      			for(int i=0; i<parser.getAttributeCount(); i++)
	      				element.sAttribut(parser.getAttributeLocalName(i), parser.getAttributeValue(i));
	      			this.zuhoererStart(pfadname, element);
	      			this.zuhoererStart("ALLE", element);
	      		}
		    }
		    if(event==XMLStreamConstants.CHARACTERS) {
	      		if(!parser.isWhiteSpace()) {
	      			String pfadname=iString(pfad);
	      			element=offen.gib(pfadname, null);
//	      			Ausgabe.debug(pfad, pfadname, element, parser.getText());
	      			if(element==null)
	      				continue;
	      			element.plusText(parser.getText());
	      			this.zuhoererStart(pfadname, element);
	      			this.zuhoererStart("ALLE", element);
	      		}
		    }
			if(event==XMLStreamConstants.END_ELEMENT) {
	      		String pfadname=iString(pfad);
      			element=offen.gib(pfadname, null);
//      			Ausgabe.debug("START-ENDE", pfadname, offen);
      			if(element!=null) {
	      			offen.minus(pfadname);
	      			this.zuhoererStart("ENDE:"+pfadname, element);
	      		}
	      		String pfadm=pfad.minusLetzten();
	      		if(!pfadm.toLowerCase().equals(parser.getLocalName().toLowerCase()))
	      			Fehler.sonstige.da_Verboten("XML-Format-Fehler", offen, pfadm, parser.getLocalName());
			}
		}

	}

	private String iString(String[] sa) {
		return "/"+WandleArray.zuString("/", sa);
	}

	private String iString(S_Folge<String> sa) {
		return "/"+WandleFolge.zuText("/", sa);
	}
}
